/* eslint-disable camelcase */
import * as THREE from 'three'
import { lon2xyz } from './helper'

const R = 70

function circleLine (x: number, y: number, r: number, startAngle: number, endAngle: number) {
  // console.log(x, y, r, startAngle, endAngle)
  const geometry = new THREE.BufferGeometry()
  // THREE.ArcCurve创建圆弧曲线
  const arc = new THREE.ArcCurve(x, y, r, startAngle, endAngle, false)
  // getSpacedPoints是基类Curve的方法，返回一个vector2对象作为元素组成的数组
  const points = arc.getSpacedPoints(50) // 分段数50，返回51个顶点
  geometry.setFromPoints(points)// setFromPoints方法从points中提取数据改变几何体的顶点属性vertices
  return new THREE.Line(geometry, new THREE.LineBasicMaterial({ color: 0xffffff }))
}

// 计算两点之间和球心构成夹角的弧度值方法
function GetAngleAOB (A: THREE.Vector3, B: THREE.Vector3, O: THREE.Vector3) {
  // dir1、dir2：球面上两个点和球心构成的方向向量
  const dir1 = A.clone().sub(O).normalize()
  const dir2 = B.clone().sub(O).normalize()
  // return A.angleTo(B)
  return Math.acos(dir1.clone().dot(dir2))
}

// 求p1, p2, p3三个点的外接圆圆心
function threePointCenter (p1: THREE.Vector3, p2: THREE.Vector3, p3: THREE.Vector3) {
  const L1 = p1.lengthSq()// 到坐标原点距离的平方
  const L2 = p2.lengthSq()
  const L3 = p3.lengthSq()
  const x1 = p1.x; const y1 = p1.y; const x2 = p2.x; const y2 = p2.y; const x3 = p3.x; const y3 = p3.y
  const S = x1 * y2 + x2 * y3 + x3 * y1 - x1 * y3 - x2 * y1 - x3 * y2
  const x = (L2 * y3 + L1 * y2 + L3 * y1 - L2 * y1 - L3 * y2 - L1 * y3) / S / 2
  const y = (L3 * x2 + L2 * x1 + L1 * x3 - L1 * x2 - L2 * x3 - L3 * x1) / S / 2
  // 三点外接圆圆心坐标
  return new THREE.Vector3(x, y, 0)
}

function createFlyLine (r: number, startAngle: number, endAngle: number) {
  const geometry = new THREE.BufferGeometry()
  const arc = new THREE.ArcCurve(0, 0, r, startAngle, endAngle, false)
  const pointsArr = arc.getSpacedPoints(50)
  geometry.setFromPoints(pointsArr)
  const percentArr = []
  for (let i = 0; i < pointsArr.length; i++) {
    percentArr.push(i / pointsArr.length)
  }
  const percentAttribue = new THREE.BufferAttribute(new Float32Array(percentArr), 1)
  geometry.attributes.percent = percentAttribue

  const colorArr = []
  for (let i = 0; i < pointsArr.length; i++) {
    const color1 = new THREE.Color(0xffffff)
    const color2 = new THREE.Color(0xffff00)
    const color = color1.lerp(color2, i / pointsArr.length)
    colorArr.push(color.r, color.g, color.b)
  }

  geometry.attributes.color = new THREE.BufferAttribute(new Float32Array(colorArr), 3)

  const material = new THREE.PointsMaterial({
    size: 3.0,
    vertexColors: true
  })

  material.onBeforeCompile = function (shader) {
    shader.vertexShader = shader.vertexShader.replace(
      'void main() {',
      [
        'attribute float percent;',
        'void main() {'
      ].join('\n')
    )

    shader.vertexShader = shader.vertexShader.replace(
      'gl_PointSize = size;',
      [
        'gl_PointSize = percent * size;'
      ].join('\n')
    )
  }
  const FlyLine = new THREE.Points(geometry, material)
  return FlyLine
}

function arcXOY (startPoint: THREE.Vector3, endPoint: THREE.Vector3) {
  const middleV3 = new THREE.Vector3().addVectors(startPoint, endPoint).multiplyScalar(0.5)
  // console.log(startPoint, endPoint, middleV3)

  const dir = middleV3.clone().normalize()
  const angle = GetAngleAOB(startPoint, endPoint, new THREE.Vector3(0, 0, 0))
  const arcTopCoord = dir.multiplyScalar(R + angle * R * 0.1)

  const arcCenter = threePointCenter(startPoint, endPoint, arcTopCoord)
  const flyArcR = Math.abs(arcCenter.y - arcTopCoord.y)

  const flyRadianAngle = GetAngleAOB(startPoint, new THREE.Vector3(0, -1, 0), arcCenter)
  const startAngle = -Math.PI / 2 + flyRadianAngle
  const endAngle = Math.PI - startAngle

  const arcline = circleLine(arcCenter.x, arcCenter.y, flyArcR, startAngle, endAngle)
  // (arcline as any).center = arcCenter; // 飞线圆弧自定一个属性表示飞线圆弧的圆心
  // (arcline as any).topCoord = arcTopCoord // 飞线圆弧自定一个属性表示飞线圆弧中间也就是顶部坐标

  const flyAngle = (endAngle - startAngle) / 7 // 设置飞线圆弧的弧度和轨迹线弧度相关
  const flyLine = createFlyLine(flyArcR, startAngle, startAngle + flyAngle)
  flyLine.position.y = arcCenter.y// 平移飞线圆弧和飞线轨迹圆弧重合
  // 飞线段flyLine作为飞线轨迹arcLine子对象，继承飞线轨迹平移旋转等变换
  arcline.add(flyLine);
  // // 飞线段运动范围startAngle~flyEndAngle
  (flyLine as any).flyEndAngle = endAngle - startAngle - flyAngle;
  (flyLine as any).startAngle = startAngle;
  // // 飞线段当前角度位置，设置随机值让飞线不是同步运动
  // (flyLine as any).AngleZ = arcline.flyEndAngle * Math.random();
  (arcline as any).flyLine = flyLine
  return arcline
}

function _3Dto2D (startSphere: THREE.Vector3, endSphere: THREE.Vector3) {
  // 第一次旋转
  const origin = new THREE.Vector3(0, 0, 0)
  const startDir = startSphere.clone().sub(origin)
  const endDir = endSphere.clone().sub(origin)
  const normal = startDir.clone().cross(endDir).normalize()
  const xoyNormal = new THREE.Vector3(0, 0, 1)// XOY平面的法线
  const quaternion3D_XOY = new THREE.Quaternion().setFromUnitVectors(normal, xoyNormal)
  const startSphereXOY = startSphere.clone().applyQuaternion(quaternion3D_XOY)
  const endSphereXOY = endSphere.clone().applyQuaternion(quaternion3D_XOY)
  // 第二次旋转
  const middleV3 = startSphereXOY.clone().add(endSphereXOY).multiplyScalar(0.5)
  const midDir = middleV3.clone().sub(origin).normalize()
  const yDir = new THREE.Vector3(0, 1, 0)
  const quaternionXOY_Y = new THREE.Quaternion().setFromUnitVectors(midDir, yDir)

  const startSpherXOY_Y = startSphereXOY.clone().applyQuaternion(quaternionXOY_Y)
  const endSphereXOY_Y = endSphereXOY.clone().applyQuaternion(quaternionXOY_Y)

  const quaternionInverse = quaternion3D_XOY.clone().invert().multiply(quaternionXOY_Y.clone().invert())
  return {
    // 返回两次旋转四元数的逆四元数
    quaternion: quaternionInverse,
    // 两次旋转后在XOY平面上关于y轴对称的圆弧起点和结束点坐标
    endPoint: endSphereXOY_Y,
    startPoint: startSpherXOY_Y
  }
}

export function flyArc (lon1: number, lat1: number, lon2: number, lat2: number) {
  const sphereCoord1 = lon2xyz(R, lon1, lat1)
  const startSphereCoord = new THREE.Vector3(sphereCoord1.x, sphereCoord1.y, sphereCoord1.z)
  const sphereCoord2 = lon2xyz(R, lon2, lat2)
  const endSphereCoord = new THREE.Vector3(sphereCoord2.x, sphereCoord2.y, sphereCoord2.z)

  const startEndQua = _3Dto2D(startSphereCoord, endSphereCoord)
  const arcline = arcXOY(startEndQua.startPoint, startEndQua.endPoint)
  arcline.quaternion.multiply(startEndQua.quaternion)
  return arcline
}

export default function lineFlyDraw (scene: THREE.Scene) {
  const num = 10
  for (let i = 1; i < num; i++) {
    const startAngle = Math.PI / 2 / num * i// 圆弧起点和坐标原点构成的角度
    const x = R * Math.cos(startAngle) // 飞线圆弧起点横坐标
    const y = R * Math.sin(startAngle) // 飞线圆弧起点纵坐标

    const startPoint = new THREE.Vector3(x, y, 0)
    const endPoint = new THREE.Vector3(-x, y, 0)

    scene.add(arcXOY(startPoint, endPoint)) // 飞线插入场景中
  }
}
